package org.pz.controller;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;

/**
 * 锁相关接口
 * */
@Slf4j
@RestController
@RequestMapping("/lock")
public class LockController {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * lock.lock() 获取锁，获取不到一直等
     * lock.unlock() 解锁，要放在 finally 中，否则容易死锁
     * */
    @RequestMapping("/normal/{key}")
    public void normal(@PathVariable String key) {
        log.info("normal 加锁：{}", key);
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try{
            //模拟业务，使用 10s
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    /**
     * lock.lock(long leaseTime, TimeUnit tu) 尝试获取锁，并设置持有锁时间，时间一到会自动解锁
     * */
    @RequestMapping("/limit/{key}")
    public void limit(@PathVariable String key) {
        log.info("limit 加锁：{}", key);
        RLock lock = redissonClient.getLock(key);
        //获取锁，并持有锁 5s
        lock.lock(5, TimeUnit.SECONDS);
        try{
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    /**
     * 获取锁，立马解锁
     * */
    @RequestMapping("/lock/{key}")
    public void lock(@PathVariable String key) {
        log.info("无业务模拟，仅做尝试获取锁：{}", key);
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        lock.unlock();
    }

    /**
     * tryLock() 尝试获取锁一次，成功返回 true，失败返回 false
     * */
    @RequestMapping("/try/{key}")
    public boolean tryLock(@PathVariable String key) {
        log.info("tryLock(), 尝试获取锁：{}", key);
        RLock lock = redissonClient.getLock(key);
        boolean b = lock.tryLock();
        if(b && lock.isHeldByCurrentThread()){
            lock.unlock();
        }
        return b;
    }

    /**
     * tryLock(long time, TimeUnit unit) 在一定时间内，尝试获取锁
     * tryLock(long waitTime, long leaseTime, TimeUnit unit) 这个方法多了 leaseTime，锁持有时间，一般不用，会有看门狗机制
     * */
    @RequestMapping("/tryTime/{key}")
    public boolean tryTimeLock(@PathVariable String key) throws InterruptedException {
        log.info("tryLock(time), 3s内尝试获取锁：{}", key);
        RLock lock = redissonClient.getLock(key);
        boolean b = lock.tryLock(3, TimeUnit.SECONDS);
        if(b && lock.isHeldByCurrentThread()){
            lock.unlock();
        }
        return b;
    }
}
